#include "Comenzador.h"
#include "Ordenador_Archivo.h"
#include "Indexador.h"
#include "Constantes.h"
#include "CompresorDelta.h"
#include "Reg_Arch.h"
#include "Analizador_Sintactico.h"
#include <fstream>
#include <sstream>
#include <math.h>

Comenzador::Comenzador() {}

// Chequeo si ya indexe el directorio
bool Comenzador::existe_dir(std::string directorio, std::fstream *directorios) {
	std::string linea;
	directorios->clear();
	directorios->seekg(0, std::ios_base::beg);
	//std::cout << "REINDEXANDO " << directorio << std::endl;
	while(getline(*directorios, linea,'\n')) {
		linea.erase(linea.size() - 2, 3);
		if(linea.at(linea.size() - 1) == ' ')
			linea.erase(linea.size() - 1, 1);
		//std::cout << "linea: " << linea << std::endl;
		if(linea.compare(directorio) == 0) {
			(*directorios).seekg(0, std::ios_base::beg);
			if(directorios->eof() == directorios->fail())
				directorios->clear();
			return true;
		}
	}
	directorios->clear();
	(*directorios).seekg(0, std::ios_base::beg);
	return false;
}

// Chequeo si ya indexe algun subdirectorio de "directorio"
bool Comenzador::existe_subdir(std::string directorio, std::fstream *directorios) {
	std::string linea;
	directorios->clear();
	directorios->seekg(0, std::ios_base::beg);
	while(getline(*directorios, linea)) {
		linea.erase(linea.size() - 2, 2);
		if(linea.find_first_of(directorio) != std::string::npos)
			if(linea.size() > directorio.size())
				if(linea.at(directorio.size()) == '/')
					if(linea.compare(directorio.c_str()) > 0) {
						(*directorios).seekg(0, std::ios_base::beg);
						if(directorios->eof() == directorios->fail())
							directorios->clear();
						return true;
					}
	}
	directorios->clear();
	(*directorios).seekg(0, std::ios_base::beg);

	return false;
}

// Carga los terminos del archivos al árbol
bool Comenzador::cargarTerminos(std::fstream *terminos, ArbolTrie *arbol) {
	std::string linea, termino, doc, frecu;
	size_t pos = 0;
	CompresorDelta compresor;
	bool cargado = false;
	unsigned int num_docu = 0, frecuencia = 0;
	unsigned char *num_doc = NULL, *frec = NULL;
	while(getline(*terminos, linea, '\n')) {
		pos = linea.find_first_of(" ");
		termino = linea.substr(0, pos);
		linea.erase(0, pos + 1);
		while(linea.size() > 0) {
			cargado = true;
			std::stringstream ss_doc, ss_frec;
			// Busco el número de documento
			pos = linea.find_first_of(" ");
			doc = linea.substr(0, pos);
			ss_doc << doc;
			ss_doc >> num_docu;
			compresor.comprimir(num_docu, &num_doc);
			linea.erase(0, pos + 1);

			// Busco la frecuencia
			pos = linea.find_first_of(" ");
			if(pos == std::string::npos) {
				frecu = linea.substr(0, linea.size());
				ss_frec << frecu;
				ss_frec >> frecuencia;
				linea.erase(0, linea.size());
			}
			else {
				frecu = linea.substr(0, pos);
				ss_frec << frecu;
				ss_frec >> frecuencia;
				linea.erase(0, pos + 1);
			}
			compresor.comprimir(frecuencia, &frec);
			//std::cout << "Término: " << termino << " Doc: " << num_docu << " Frec: " << frecuencia << std::endl;
			arbol->insertarTermino(termino, num_doc, frec);
			frec = NULL;
			num_doc = NULL;
			//std::cout << linea << std::endl;
			//getchar();
		}
	}
	terminos->close();
	terminos->open(nom_arch_terminos, std::ios::trunc | std::ios::in | std::ios::out);
	return cargado;
}

// Muevo el contenido de nom_arch1 a nom_arch2
void Comenzador::mover_archivos(std::string nom_arch1, std::string nom_arch2) {
	eliminar_archivo(nom_arch1);
	renombrar_archivo(nom_arch1, nom_arch2);
	eliminar_archivo(nom_arch2);
}

void Comenzador::eliminar_arch_auxiliares(std::fstream *arch_docu) {
	std::string nom_arch_aux, max_num_doc;
	std::stringstream ss, ss1;
	int num_doc = 0, max = 0;

	max = obtener_num_doc(arch_docu);

	while(max > num_doc) {
		num_doc++;
		nom_arch_aux = ruta_aux;
		nom_arch_aux = "arch_aux";
		ss << num_doc;
		nom_arch_aux += ss.str();
		nom_arch_aux += ".txt";
		//std::cout << "Archivo a elminar: " << nom_arch_aux << std::endl;
		//std::cout << eliminar_archivo(nom_arch_aux) << std::endl;
		ss.str("");
	}

	remove(nom_arch_aux1);
	remove(nom_arch_aux2);
	remove(nom_arch_dir_aux);
}

void Comenzador::borrarTodo(std::fstream *arch_docu) {
	std::string nom_arch_aux, max_num_doc;
	std::stringstream ss, ss1;
	int num_doc = 0, max = 0;

	arch_docu->seekg(0, std::ios_base::beg);
	max = obtener_num_doc(arch_docu);

	while(max > num_doc) {
		num_doc++;
		nom_arch_aux = ruta_aux;
		nom_arch_aux += "arch_aux";
		ss << num_doc;
		nom_arch_aux += ss.str();
		nom_arch_aux += ".txt";
		//std::cout << "Archivo a elminar: " << nom_arch_aux << std::endl;
		//std::cout << eliminar_archivo(nom_arch_aux) << std::endl;
		ss.str("");
	}
	eliminar_archivo(nom_arch_aux1);
	eliminar_archivo(nom_arch_aux2);
	eliminar_archivo(nom_arch_dir_aux);
	eliminar_archivo(nom_arch_doc);
	eliminar_archivo(nom_arch_dir);
	eliminar_archivo(nom_arch_terminos);
	eliminar_archivo(nom_arch_dir_aux);

}

// Ordena los archivos de términos alfabeticamente y agrupa los q son iguales
void Comenzador::ordenar_archivos(std::fstream *arch_docu, ArbolTrie *arbol) {
	Ordenador_Archivo ordenador;
	Indexador indexador;
	std::fstream arch_aux;
	std::string nom_arch_aux, max_num_doc;
	std::stringstream ss, ss1;
	int max = 0, num_doc = 0;
	//ArbolTrie arbol;
	max = obtener_num_doc(arch_docu);

	while(max > num_doc) {
		num_doc++;
		nom_arch_aux = ruta_aux;
		nom_arch_aux += "arch_aux";
		ss << num_doc;
		nom_arch_aux += ss.str();
		nom_arch_aux += ".txt";
		std::cout << nom_arch_aux.c_str() << std::endl;
		arch_aux.open(nom_arch_aux.c_str(), std::fstream::in| std::fstream::out);
		// arch_aux.seekp(0, std::ios_base::beg);
		if(arch_aux.is_open())  {
			//std::cout << "Archivo a ordenar: " << nom_arch_aux << std::endl;
			ordenador.mezcla_natural(&arch_aux, nom_arch_aux);
			// Una vez q los ordena, agrupa los q son iguales y los indexa
			indexador.indexar_terminos(&arch_aux, nom_arch_aux, arbol, num_doc);
			arch_aux.close();
			eliminar_archivo(nom_arch_aux);
		}
		ss.str("");
	}
}

void Comenzador::renombrar_archivo(std::string nom_nuevo, std::string nom_viejo) {
	rename(nom_viejo.c_str(), nom_nuevo.c_str());
}

int Comenzador::eliminar_archivo(std::string nom_arch) {
	return(remove(nom_arch.c_str()));
}

// Une el contendio de "dir_aux" al final de "dir"
void Comenzador::unir_archivos(std::fstream *dir, std::fstream *dir_aux, std::string nom_arch_aux) {
	dir->seekp(0,std::ios_base::end);
	dir_aux->seekg(0,std::ios_base::beg);
	std::string linea;
	while(getline(*dir_aux, linea)) {
		linea += '\n';
		*dir << linea;
	}
	dir_aux->close();
	eliminar_archivo(nom_arch_aux);
}

void Comenzador::vaciar_archivo(std::string nombre, std::fstream *archivo){
	archivo->close();
	archivo->open(nombre.c_str(), std::ios::trunc);
}

int Comenzador::obtener_num_doc(std::fstream *arch) {
	std::fstream arch_aux;
	std::string nom_arch_aux, max_num_doc;
	int max = 0, cont = 0;
	bool mas_digitos = true;
	char aux;
	while(mas_digitos) {
		//arch->clear();
		arch->seekg(-2 - cont, std::ios_base::end);
		*arch >> max_num_doc;
		arch->clear();
		if(max_num_doc.size() > 0) {
			aux = max_num_doc.at(0);
			if((aux > '9') || (aux < '0')) {
				mas_digitos = false;
			}
			std::stringstream ss(max_num_doc.c_str());
			ss >> max;
			max_num_doc.clear();
			cont++;
		}
		else {
			max = -1;
			mas_digitos = false;
		}
	}
	//std::cout << "Num max doc " << max << std::endl;
	return max;
}

std::string Comenzador::continuar_aplicacion() {
	std::string opcion, aux;
	std::cout << "-b: reinicia el indexador" << std::endl;
	std::cout << "-i ''directorio'': indexa dicho directorio" << std::endl;
	std::cout << "-q ''consulta'': busca consulta en el indice" << std::endl;
	std::cout << "-s : finaliza la aplicación" << std::endl;
	//std::cin >> opcion;
	getline(std::cin, opcion, '\n');
	for(unsigned int i = 0; i < opcion.size(); i++)
		aux[i] = tolower(opcion[i]);
	if(aux.compare("-s") == 0)
		opcion = aux;
	return opcion;
}

// Busca los terminos en el árbol y realiza los calculos del p.i.
void Comenzador::consulta(std::string terminos, ArbolTrie *arbol, int N, std::fstream* datos_arch, std::fstream *documentos) {
	std::list<FreqDocumento> *list = NULL;
	std::list<FreqDocumento>::iterator it;
	std::list<double> pesos_documentos;
	CompresorDelta compresor;
	Reg_Arch reg_datos;
	Analizador_Sintactico analizador;
	std::string nom_arch, termino;
	unsigned int cant_terminos = 0, num_termino = 0, *num_terminos_obligatorios = NULL;
	char *excluir_docus = NULL, **docus_obligatorios = NULL;
	/*
	 *  Se excluye un documento si al realizar la busqueda: singer britney -spears
	 *  en dicho documento aparece spears
	 */
	bool excluir_documento = false;
	/**
	 * 	Si al realizar la busqueda: singer britney +spears
	 *  en dicho documento no aparece spears, ese doc no se tiene en cuenta
	 */
	bool termino_obligatorio = false;
	bool hubo_mas = false;


	// indica la importancia del término en el documento
	unsigned int *importancia = NULL;
	unsigned int ft = 0, ftd = 0, num_doc = 0;
	double aux = 0.0, wtd = 0;
	double *peso_de_docus = new double[N];

	cant_terminos = obtener_cant_terminos(terminos);
	importancia = new unsigned int[cant_terminos];

	docus_obligatorios = new char*[cant_terminos];
	num_terminos_obligatorios = new unsigned int[cant_terminos];

	//docus_obligatorios
	for(unsigned int i = 0; i < cant_terminos; i++) {
		importancia[i] = 1;
		num_terminos_obligatorios[i] = 0;
		docus_obligatorios[i] = new char[N];
	}

	obtener_importancias(&importancia, terminos);

	for(int i = 0; i < N; i++) {
		peso_de_docus[i] = 0;
		//docus_obligatorios[i] = '0';
	}

	for(unsigned int i = 0; i < cant_terminos; i++)
		for(int j = 0; j < N; j++)
			docus_obligatorios[i][j] = '0';

	std::cout << "Cantidad de documentos " << N << std::endl;
	std::cout << "Cantidad de terminos " << cant_terminos << std::endl;

	while((termino = obtener_termino(&terminos)).compare(" ") != 0) {
		if(termino.at(0) == '+') {
			termino_obligatorio = true;
			hubo_mas = true;
		}
		analizador.Convertir_palabra(&termino);
		if(termino.at(termino.size() - 1) <= '9')
			termino.erase(termino.size() - 1,1);
		//std::cout << "Termino: " << termino << std::endl;
		if((list = arbol->buscarTermino(termino)) != NULL) {
			ft = list->size();
			for(it = list->begin(); it != list->end(); it++) {
				if((it->getFrecuencia() != NULL) && (it->getNumDocumento() != NULL)) {
					ftd = compresor.descompresor(it->getFrecuencia());
					num_doc = compresor.descompresor(it->getNumDocumento());
					//std::cout << "Num doc: " << num_doc << std::endl;
					//std::cout << "Num ter: " << num_termino << std::endl;
					//std::cout << "Frecuencia: " << ftd << std::endl;
					//std::cout << "Pos documento: " << reg_datos.getPos() << std::endl;
					//std::cout << "Importancia: " << importancia[num_termino] << std::endl;
					aux = log10(((double)N/ft) + 1);
					wtd = aux * ftd; // wtd = aux * ftd, aux es el peso de termino "t" en el doc "d"
					datos_arch->seekg((num_doc - 1) * sizeof(Reg_Arch), std::ios_base::beg);
					datos_arch->read(reinterpret_cast<char *>(&reg_datos), sizeof(reg_datos));
					peso_de_docus[num_doc - 1] +=  ((double)(((double)(wtd / reg_datos.getPeso())) * importancia[num_termino]));
					if(termino_obligatorio) {
						// Si num_terminos_obligatorios[i] == 1 => el termino "i" es obligatorio
						num_terminos_obligatorios[num_termino] = 1;
						docus_obligatorios[num_termino][num_doc - 1] = '1';
					}

				}
			}
		}
		termino_obligatorio = false;
		num_termino++;
	}

	//bool excluir = false;
	if(hubo_mas) {
		for(int i = 0; i < N; i++) {
			for(unsigned int j = 0; j < cant_terminos; j++) {
				if(num_terminos_obligatorios[j] == 1) {
					if(docus_obligatorios[j][i] == '0') {
						//excluir = true;
						docus_obligatorios[0][i] = '0';
					}
					/*else {
						if(j > 0)
							if(docus_obligatorios[0][i] )
							docus_obligatorios[0][i] = '1';
					}*/
				}
			}
			//excluir = false;
		}
	}
	// En la fila 0 guardo los documendos q debo tener en cuenta

	for(int i = 0; i < N; i++) {
		if(peso_de_docus[i] != 0) {
			if((docus_obligatorios[0][i] == '1') || (!hubo_mas)) {
				datos_arch->seekg(i * sizeof(Reg_Arch), std::ios_base::beg);
				datos_arch->read(reinterpret_cast<char *>(&reg_datos), sizeof(reg_datos));
				documentos->seekg(reg_datos.getPos(), std::ios_base::beg);
				getline(*documentos, nom_arch, '\n');
				std::cout << nom_arch << std::endl;
				pesos_documentos.push_back(peso_de_docus[i]);
				std::cout << "Doc " << i + 1 << " peso:  " << peso_de_docus[i] << endl;
			}
		}
	}

	pesos_documentos.sort();

	documentos->seekg(0, std::ios_base::end);
	documentos->clear();
	datos_arch->seekg(0, std::ios_base::end);
	for(unsigned int i = 0; i < cant_terminos; i++)
		delete[] docus_obligatorios[i];
	delete[] docus_obligatorios;
	delete[] peso_de_docus;
	delete[] importancia;
}

std::string Comenzador::obtener_termino(std::string *consulta) {
	std::string termino = " ";
	size_t pos_espacio = consulta->find_first_of(" ");
	if(consulta->size() > 0) {
		if(pos_espacio != std::string::npos) {
			termino = consulta->substr(0, pos_espacio);
			consulta->erase(0, pos_espacio + 1);
		}
		else {
			termino = *consulta;
			consulta->clear();
		}
	}
	return termino;
}

// Chequea la cantidad de terminos de la consulta
unsigned int Comenzador::obtener_cant_terminos(std::string consulta) {
	size_t pos_espacio = 0, aux = 0;
	unsigned int cant_terminos = 0;
	while(((aux = consulta.find(" ", pos_espacio + 1)) > pos_espacio)  && (aux != std::string::npos)) {
		pos_espacio = aux;
		cant_terminos++;
	}
	//std::cout << "CANT " << cant_terminos << std::endl;
	cant_terminos++;
	return cant_terminos;
}

// Se fija si los terminos a buscar son más relevantes q otros (^2, ^4, etc)
void Comenzador::obtener_importancias(unsigned int **importancias, std::string consulta) {
	size_t pos_potencia = 0, aux = 0, pos_espacio = 0;
	unsigned int num_termino = 0, importancia = 1;
	char op;
	bool unico_termino = true;
	while(((aux = consulta.find(" ", pos_espacio)) > pos_espacio) && (aux != std::string::npos)) {
		pos_espacio = aux;
		//std::cout << consulta.at(pos_espacio - 2) << std::endl;
		if(consulta.at(pos_espacio - 2) == '^') {
			pos_potencia = pos_espacio - 2;
			op = consulta.at(pos_potencia + 1);
		}
		if((op <= '9') && (op >= '0')) {
			std::stringstream ss;
			ss << op;
			ss >> importancia;
			//cout << importancia << endl;
			(*importancias)[num_termino] = importancia;
		}
		num_termino++;
		unico_termino = false;
	}
	op = consulta.at(consulta.size() - 2);
	if(op == '^') {
		op = consulta.at(consulta.size() - 1);
		if((op <= '9') && (op >= '0')) {
			std::stringstream ss;
			ss << op;
			ss >> importancia;
			//cout << importancia << endl;
			(*importancias)[num_termino] = importancia;
		}
	}
}

Comenzador::~Comenzador() {}

